﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using LINQPad.OpenAccess.Helpers;
using Telerik.OpenAccess;
using Telerik.OpenAccess.Metadata;

namespace LINQPad.OpenAccess.Metadata.Reflection
{
    /// <summary>
    /// <see cref="IReflectionService"/>
    /// </summary>
    public partial class ReflectionService : IReflectionService
    {
        #region IReflectionServices Members

        /// <summary>
        /// Gets the context types from the specified assembly.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns>
        /// The context types.
        /// </returns>
        public IEnumerable<Type> GetContextTypesFromAssembly( Assembly assembly )
        {
            Guard.ArgumentNotNull( assembly, "assembly" );
            return assembly.GetTypes( )
                           .Where( IsContextType );
        }
        /// <summary>
        /// Gets the entity sets from context the specified context.
        /// </summary>
        /// <param name="contextType">Type of the context.</param>
        /// <returns>
        /// The entity sets.
        /// </returns>
        public IEnumerable<EntitySetInfo> GetEntitySetsFromContext( Type contextType )
        {
            Guard.ArgumentNotNull( contextType, "contextType" );

            return contextType.GetProperties( )
                              .Where( IsEntitySetProperty )
                              .Select
                                (
                                    prop => new EntitySetInfo( prop.DeclaringType, prop.PropertyType.GetGenericArguments( )[0], prop )
                                );
        }
        /// <summary>
        /// Gets the metadata source types from assembly.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns>
        /// The metadata source types.
        /// </returns>
        public IEnumerable<Type> GetMetadataSourceTypesFromAssembly( Assembly assembly )
        {
            Guard.ArgumentNotNull( assembly, "assembly" );
            return assembly.GetTypes( )
                           .Where( IsMetadataSourceType );
        }
        /// <summary>
        /// Determines whether the specified type is an open access context type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>
        ///   <c>true</c> if the specified type is an open access context type; otherwise, <c>false</c>.
        /// </returns>
        public bool IsContextType( Type type )
        {
            if ( type == null )
                return false;

            return !type.IsAbstract && typeof( OpenAccessContextBase ).IsAssignableFrom( type );
        }
        /// <summary>
        /// Determines whether the specified property is an entity set.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <returns>
        ///   <c>true</c> if the specified property is an entity set; otherwise, <c>false</c>.
        /// </returns>
        public bool IsEntitySetProperty( PropertyInfo property )
        {
            if ( property == null )
                return false;
            else if ( !this.IsContextType( property.DeclaringType ) )
                return false;

            var propertyType = property.PropertyType;
            if ( !propertyType.IsInterface || !propertyType.IsGenericType )
                return false;

            return propertyType.GetGenericTypeDefinition( ) == typeof( IQueryable<> );
        }
        /// <summary>
        /// Determines whether the specified type is a metadata source type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>
        ///   <c>true</c> if the specified type is a metadata source type; otherwise, <c>false</c>.
        /// </returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public bool IsMetadataSourceType( Type type )
        {
            if ( type == null )
                return false;

            return !type.IsAbstract && typeof( MetadataSource ).IsAssignableFrom( type );
        }

        #endregion
    }
}
